「NestJS」をAWS Lambda + API Gatewayで動かす
どうも!大阪オフィスの西村祐二です。
個人的にフロントエンドはAngularを使ってよく開発しています。
AngularはTypeScriptがデフォルトなので、APIのレスポンスを型定義するのですが、サーバーサイドもTypeScriptにして型定義を共有できたら良さそうだなと思っていました。
そこで最近、TypeScript製Node.jsフルスタックフレームワーク「NestJS」を調べています。
通常であれば、ECSやFargeteで動かすのが筋かと思いますが、Lambdaの機能改善によって、ちょっとした検証環境であれば、Lambdaで動かすこともありなのかなと思うようになってきました。
そこで、今回はその「NestJS」をAWS Lambda + API Gatewayのサーバーレス構成で動かす方法をご紹介したいと思います。
あくまでも、こういうこともできるという一案であり、この構成についてはいろいろ議論や検討の余地があるところかと思いますので、ご考慮いただけると幸いです。
また、どういう構成がいいかなどみんなで議論できると嬉しいです。
環境
- Node: 12.13.0
- NestJS: 6.10.1
- Serverless Framework: 1.57.0
作業概要
Angular同様にNestJSもCLIが提供されていますので、それを使ってプロジェクトを作成します。
LambdaへのデプロイはServerless Frameworkを使ってデプロイします。
node_modules
の容量が大きくなってしまうので、Lambda Layersを利用します。
Lambda Layersへのデプロイを簡単にするためにプラグインの「serverless-layers」を使用します。
試してみる
環境準備
パッケージをインストールします。
$ npm i -g @nestjs/cli $ npm i -g serverless
NestJSのプロジェクト作成
NestJSのCLIを使ってプロジェクトを作成します。
今回は「serverless-nestjs」としています。
$ nest new serverless-nestjs ? Which package manager would you ❤️ to use? npm
プロジェクトが作成できたら、ローカルで実行しています。
$ cd serverless-nestjs $ npm run start
ブラウザをひらき、「http://localhost:3000」にアクセスし
Hello World!
と表示されればOKです。
次はNestJSがLambdaで動くようにハンドラーファイルを作成します。
Lambda用のハンドラーファイルを作成
必要なライブラリをインストールします。
$ npm i aws-lambda aws-serverless-express express
API Gatewayからデータを受けとるためのLambdaハンドラーを作成します。
import { Context, Handler } from 'aws-lambda'; import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { Server } from 'http'; import { ExpressAdapter } from '@nestjs/platform-express'; import * as serverless from 'aws-serverless-express'; import * as express from 'express'; let cachedServer: Server; function bootstrapServer(): Promise<Server> { const expressApp = express(); const adapter = new ExpressAdapter(expressApp); return NestFactory.create(AppModule, adapter) .then(app => app.enableCors()) .then(app => app.init()) .then(() => serverless.createServer(expressApp)); } export const handler: Handler = (event: any, context: Context) => { if (!cachedServer) { bootstrapServer().then(server => { cachedServer = server; return serverless.proxy(server, event, context); }); } else { return serverless.proxy(cachedServer, event, context); } };
Serverless Frameworkの設定
プラグインをインストールします。
$ npm i -D serverless-layers
Layerをデプロイするために事前にS3にバケットを作成しておきます。
今回は「nestjs-lambda-layers」というバケットを作成しておきました。
NestJSはビルドコマンドでTypeScriptのファイルをJSに変換し、dist/
に出力してくれるので、そのファイルをデプロイパッケージに含めデプロイします。
node_modules
のファイルはserverless-layers
を使ってLayerとして紐付けます。
作成するAPI GatewayはAnyとして受け取ったリクエストはすべてLambdaで動くNestJSに渡すようにします。
service: serverless-nestjs provider: name: aws runtime: nodejs10.x region: ap-northeast-1 plugins: - serverless-layers custom: serverless-layers: layersDeploymentBucket: nestjs-lambda-layers package: individually: true include: - dist/** exclude: - '**' functions: index: handler: dist/index.handler events: - http: cors: true path: '/' method: any - http: cors: true path: '{proxy+}' method: any
これで準備完了です。
デプロイしてみる
下記コマンドを実行して、デプロイしてみましょう。
$ nest build && sls deploy Serverless: [LayersPlugin]: Downloading package.json from bucket... Serverless: [LayersPlugin]: package.json does not exists at bucket... Serverless: [LayersPlugin]: Dependencies has changed! Re-installing... npm WARN [email protected] No description npm WARN [email protected] No repository field. > @nestjs/[email protected] postinstall /Users/yuji/study/nestjs/serverless-nestjs/.serverless/layers/nodejs/node_modules/@nestjs/core > opencollective || exit 0 added 161 packages from 151 contributors and audited 885260 packages in 6.078s found 0 vulnerabilities Serverless: [LayersPlugin]: Created layer package /Users/yuji/study/nestjs/serverless-nestjs/.serverless/serverless-nestjs-dev.zip (13.6 MB) Serverless: [LayersPlugin]: Uploading layer package... Serverless: [LayersPlugin]: OK... Serverless: [LayersPlugin]: New layer version published... Serverless: [LayersPlugin]: Uploading remote package.json... Serverless: [LayersPlugin]: OK... Serverless: [LayersPlugin]: Associating layers... Serverless: [LayersPlugin]: function.index - arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxxxxxxx:layer:serverless-nestjs-dev:2 Serverless: Packaging service... Serverless: Excluding development dependencies... Serverless: Creating Stack... Serverless: Checking Stack create progress... ........ Serverless: Stack create finished... Serverless: Uploading CloudFormation file to S3... Serverless: Uploading artifacts... Serverless: Uploading service index.zip file to S3 (42.58 KB)... Serverless: Validating template... Serverless: Updating Stack... Serverless: Checking Stack update progress... ....................................... Serverless: Stack update finished... Service Information service: serverless-nestjs stage: dev region: ap-northeast-1 stack: serverless-nestjs-dev resources: 14 api keys: None endpoints: ANY - https://xxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/ ANY - https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/{proxy+} functions: index: serverless-nestjs-dev-index layers: None Serverless: [LayersPlugin]: function.index = layers.arn:aws:lambda:ap-northeast-1:xxxxxxxx:layer:serverless-nestjs-dev:2 Serverless: Run the "serverless" command to setup monitoring, troubleshooting and testing.
上記のようなログがでれば成功です!
これで、LambdaでNestJSが動く、API Gateway+Lambdaの構成ができました!簡単ですね。
デプロイしたAPIを叩いてみる
上記でAPI Gateway+Lambdaの構成ができたので実際に動かしてみたいと思います。
ログ出力のendpoints
のところに出力されたURLにブラウザからアクセスしてみましょう。
するとローカル実行で確認したのと同じように「Hello World!」が出力されるかと思います!やりましたね!
さいごに
「NestJS」をAWS Lambda + API Gatewayの構成で動かしてみました。
「NestJS」はAngularのように開発できたり、CLIでローカル実行やビルド、テストなどが実行できたりと、いろんな機能が予め用意されているので、環境構築に手間がかからず、すぐに開発に取りかかれて良いです。
さらに、Serverless Frameworkを使えば、簡単に「NestJS」が動くAPI Gateway+Lambdaの構成を構築できます。
次はAngular+NestJSで実践的なものを作ってみたいと思います。
誰かの参考になれば幸いです。